#导入仿真程序需要的类库
import random
import numpy as np
import matplotlib.pyplot as plt
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
import simpy
import pandas as pd



#对于仿真过程中的常数变量进行赋值
pop_size = 500  # 种群数量
max_value = 10  # 基因中允许出现的最大值
chrom_length = 100  # 染色体长度
pc = 0.8  # 交配概率
pm = 0.2  # 变异概率
generation = 500#迭代次数
# 平均适应度
wood_capacity = 500
initial_wood = 300
dispatch_capacity  = 500
days = 100
hours = 8
total_time = days*hours
results = []



#差分进化类函数
class Population:
    def __init__(self, min_range, max_range, dim, factor, rounds, size, object_func, CR=0.05):
        self.min_range = min_range
        self.max_range = max_range
        self.dimension = dim
        self.factor = factor
        self.rounds = rounds
        self.size = size
        self.cur_round = 1
        self.CR = CR
        self.get_object_function_value = object_func
        # 初始化种群
        self.individuality = [np.array([random.uniform(self.min_range, self.max_range) for s in range(self.dimension)])
                              for tmp in range(size)]
        self.object_function_values = [self.get_object_function_value(v) for v in self.individuality]
        self.mutant = None



#生成变异的种群
    def mutate(self):
        self.mutant = [[]]
        self.object_function_values = [self.get_object_function_value(v) for v in self.individuality]
        print(self.object_function_values)
        for i in range(self.size):
            r0, r1, r2 = 0, 0, 0
            while r0 == r1 or r1 == r2 or r0 == r2 or r0 == i:
                r0 = random.randint(0, self.size - 1)
                r1 = random.randint(0, self.size - 1)
                r2 = random.randint(0, self.size - 1)
            tmp = list(np.array(self.individuality[r0]) + (np.array(self.individuality[r1]) - np.array(self.individuality[r2])) * self.factor)
            tmp = [round(x) for x in tmp]
            for t in range(self.dimension):
                if tmp[t] > self.max_range or tmp[t] < self.min_range:
                    tmp[t] = random.randint(self.min_range, self.max_range)
            self.mutant.append(tmp)
        self.mutant = self.mutant[1:]



#交叉选择操作
    def crossover_and_select(self):
        print(self.object_function_values)
        for i in range(self.size-2):
            if (random.random() > self.CR):
                cpoint = random.randint(0, self.dimension)
                temp1 = []
                temp2 = []
                temp1.extend(self.mutant[i][0:cpoint])
                temp1.extend(self.individuality[i + 1][cpoint:self.dimension])
                temp2.extend(self.mutant[i + 1][0:cpoint])
                temp2.extend(self.individuality[i][cpoint:self.dimension])
                tmp = self.get_object_function_value(temp1)
                tmp1 = self.get_object_function_value(temp2)
                self.mutant[i]=temp1
                if tmp > self.object_function_values[i]:
                    self.individuality[i] = temp1
                    self.object_function_values[i] = tmp
                if tmp1 > self.object_function_values[i]:
                    self.individuality[i] = temp2
                    self.object_function_values[i] = tmp1



#选择群体最优个体操作
    def print_best(self):
        m = max(self.object_function_values)
        i = self.object_function_values.index(m)
        print("轮数：" + str(self.cur_round))
        print("最佳个体：" + str(self.individuality[i]))
        print("本轮种群：" + str(self.individuality))
        print("目标函数值：" + str(m))
        results.append(m)



#进化操作
    def evolution(self):
        while self.cur_round < self.rounds:
            self.mutate()
            self.crossover_and_select()
            self.print_best()
            self.cur_round = self.cur_round + 1



#simpy仿真类
class Guitar_Factory:
    def __init__(self, env):
        self.wood = simpy.Container(env, capacity=wood_capacity, init=initial_wood)
        self.wood_control = env.process(self.wood_stock_control(env))
        self.dispatch = simpy.Container(env, capacity=dispatch_capacity, init=0)
        self.dispatch_control = env.process(self.dispatch_guitars_control(env))
        self.env_status_monitor = env.process(self.env_status(env))
        self.xuqiu = []
        for i in range(20):
            self.xuqiu.append(200)
        self.day = 0
        self.store = 0
        self.lastmake = 0
        self.lastwood = 500
        self.woodstoreprice = 1.5
        self.woodcost = 1
        self.dispatchstoreprice = 2.5
        self.dispatchcunchu = 0
        self.woodcunchu = 0
        self.profit = 0
        self.producenum = []



#记录工厂原材料状态的函数
    def wood_stock_control(self, env):
        yield env.timeout(0)
        while True:
            if env.now % 8 == 0:
                yield env.timeout(8)
                if env.now % 40 == 0:
                    self.woodcunchu = (self.lastwood + self.wood.level) * self.woodstoreprice / 2 * 5
                    self.lastwood  = self.wood.level
                    self.profit-=self.woodcunchu
                if self.wood.level <=122:
                    """
                    提前期为2天，服务度为95的安全库存量为122
                    """
                    self.day += 2
                    yield env.timeout(16)
                    """
                    EOQ模型
                    """
                    yield self.wood.put(175)
                    self.profit-=1000+175*self.woodcost



#记录工厂产品的函数
    def dispatch_guitars_control(self, env):
        yield env.timeout(0)
        while True:
            if env.now % 8 == 0:
                pro = self.producenum[int(env.now / 8)]
                if (pro > 0):
                    self.store += pro
                    self.producenum.append(pro)
                    self.wood.get(pro)
                    self.profit -= 500
                    yield env.timeout(8)
                else:
                    yield env.timeout(8)
            if env.now % 40 == 0:
                self.dispatchcunchu = (self.lastmake + self.store) * self.dispatchstoreprice / 2 * 5
                self.profit -= self.dispatchcunchu
                if (self.store > self.xuqiu[int(env.now / 40)]):
                    self.profit += self.xuqiu[int(env.now / 40)] * 50
                    self.store -= self.xuqiu[int(env.now / 40)]
                else:
                    if (self.store > 0):
                        self.profit = self.profit + (
                                    self.store * 50 - (self.xuqiu[int(env.now / 40)] - self.store) * 20)
                        self.store = 0
                    else:
                        self.profit -= (self.xuqiu[int(env.now / 40)]) * 20
                pro = self.producenum[int(env.now / 8)]
                if (pro > 0):
                    self.store += pro
                    self.producenum.append(pro)
                    self.wood.get(pro)
                    self.profit -= 500
                    yield env.timeout(8)
                else:
                    yield env.timeout(8)
                self.lastmake = self.store



#记录工厂原材料与库存的函数
    def env_status(self, env):
        yield env.timeout(0)
        global status
        status = pd.DataFrame(
            columns=["datetime", "wood", "store"])
        status[
            ["datetime", "wood", "store"]] = status[
            ["datetime", "wood", "store"]].astype(int)
        while True:
            tmp = {'datetime': env.now,
                   'wood': self.wood.level,
                   'store': self.store,
                   }
            status = status.append([tmp])
            yield env.timeout(1)



#计算个体适应值的函数
def countvalueanquan(a):
    a = list(a)
    env = simpy.Environment()
    guitar_factory = Guitar_Factory(env)
    guitar_factory.producenum = a
    env.run(until=total_time)
    return guitar_factory.profit



def get_index1(lst=None, item=''):
    return [index for (index,value) in enumerate(lst) if value == item]



#生成个体数量为500的种群
pop = [[]]
for i in range(500):
    a = list(np.random.randint(0, 2, size=100))
    pop1 =[]
    for j in (a):
        if j==1:
            pop1.append(random.randint(0,100))
        else:
            pop1.append(0)
    pop.append(pop1)
pop = pop[1:]



#仿真过程
p = Population(min_range=0, max_range=100, dim=100, factor=0.95, rounds=100, size=200,object_func=countvalueanquan)
p.individuality = pop
print(p.individuality)
p.evolution()
X = []
for i in range(p.rounds-1):
    X.append(i)
plt.figure(figsize=[20,10],dpi = 100)
plt.plot(X, results,label = "差分进化")
plt.title("生产数量可变运用差分进化的迭代结果")
plt.legend()
plt.show()
np1 = np.array(p.individuality)
np.save('pop3.npy',np1)

